package com.ruyuan.engine.router;
import com.ruyuan.engine.pojo.*;
import com.ruyuan.engine.service.*;
import com.ruyuan.engine.utils.RuleCommonProcessUtils;
import org.apache.commons.lang.time.DateUtils;
import org.apache.flink.api.common.state.ListState;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

/**
 * 查询的路由
 * */
public class Router {

    protected static final Logger log = LoggerFactory.getLogger(Router.class);

    /**
     * 注入基础查询服务
     * */
    private BaseRuleService baseRuleService;
    private CombinationRuleService combinationRuleService;
    private SequenceRuleService sequenceRuleService;
    private CombinationRuleService combinationRuleServiceClickHouseManagerImpl;
    private SequenceRuleService sequenceRuleServiceClickHouseManagerImpl;
    ListState<ClientLog> clientLogListState;

    public Router(ListState<ClientLog> listState) {
        this.clientLogListState = listState;
        baseRuleService = new BaseRuleServiceImpl();
        combinationRuleService = new CombinationRuleServiceImpl(clientLogListState);
        sequenceRuleService = new SequenceRuleServiceImpl(clientLogListState);
        combinationRuleServiceClickHouseManagerImpl = new CombinationRuleServiceClickHouseManagerImpl();
        sequenceRuleServiceClickHouseManagerImpl = new SequenceRuleServiceClickHouseManagerImpl();
    }

    /**
     * 基本规则条件查询
     * */
    public boolean mathRuleByCondition(ClientLog clientLog, RuleComplexParam ruleComplexParam) {
        return baseRuleService.mathRuleByCondition(clientLog,ruleComplexParam);
    }

    /**
     * 组合条件
     * 最近6个小时的查询 ListState
     * 存储超过6个小时的数据查询 clickhouse
     * 2022-1-15 13:00 -> 2022-1-18 20:00
     * 还有一部分数据：需要在listState和clickhouse做查询
     * */
    public boolean mathRuleConditionCount(String userId, RuleComplexParam ruleComplexParam) {
        /**
         * 1.规则分组
         * */
        //当前系统时间的前6个小时，需要将时间向上取整，然后取前6小时
        long agoSixHourTime = DateUtils.addHours(DateUtils.ceiling(new Date(), Calendar.HOUR), -6).getTime();

        //查缓存的
        ArrayList<RuleEventExecuteParam> listStateQuery = new ArrayList<>();
        //查clickhouse
        ArrayList<RuleEventExecuteParam> clickHouseQuery = new ArrayList<>();
        //查缓存和clickhouse的
        ArrayList<RuleEventExecuteParam> allQuery = new ArrayList<>();

        for (RuleEventExecuteParam ruleEventExecuteParam :ruleComplexParam.getCombinationRuleParams()) {
            if (ruleEventExecuteParam.getEventEndTime() < agoSixHourTime) {
                clickHouseQuery.add(ruleEventExecuteParam);
            }else if(ruleEventExecuteParam.getEventStartTime() >= agoSixHourTime) {
                listStateQuery.add(ruleEventExecuteParam);
            }else {
                allQuery.add(ruleEventExecuteParam);
            }
        }

        if (listStateQuery.size() > 0) {
            ruleComplexParam.setCombinationRuleParams(listStateQuery);
            //由combinationRuleService查询缓存
            boolean result = combinationRuleService.mathRuleConditionCount( ruleComplexParam);
            if (!result) return false;
        }

        if (clickHouseQuery.size() > 0) {
            ruleComplexParam.setCombinationRuleParams(clickHouseQuery);
            boolean result = combinationRuleServiceClickHouseManagerImpl.mathRuleConditionCount(ruleComplexParam);
            if (!result) return false;
        }

        //查询clickhouse和缓存，先查询缓存中是否存在
        for (RuleEventExecuteParam ruleEventExecuteParam :allQuery) {
            long eventStartTime = ruleEventExecuteParam.getEventStartTime();
            //查询缓存
            ruleEventExecuteParam.setEventStartTime(agoSixHourTime);
            boolean catCheResult = combinationRuleService.mathRuleConditionCount(ruleEventExecuteParam);
            //该条件满足规则就不用查询clickhouse
            if (catCheResult) continue;

            //如果上面的条件不ok,继续查询clickhouse
            ruleEventExecuteParam.setEventStartTime(eventStartTime);
            ruleEventExecuteParam.setEventEndTime(agoSixHourTime);
            boolean clickHouseReuslt = combinationRuleServiceClickHouseManagerImpl.mathRuleConditionCount(userId, ruleEventExecuteParam);
            if (!clickHouseReuslt) return false;
        }
        //匹配规则
        return true;
    }

    /**
     * 组合顺序规则
     * */
    public boolean mathRuleSequenceCount(ClientLog clientLog, RuleComplexParam ruleComplexParam) {
        //获取规则参数
        List<RuleEventExecuteParam> sequenceRuleParams = ruleComplexParam.getSequenceRuleParams();

        if (sequenceRuleParams != null && sequenceRuleParams.size() > 0) {
            //获取缓存的有效时间
            long agoSixHourTime = DateUtils.addHours(DateUtils.ceiling(new Date(), Calendar.HOUR), -6).getTime();
            //规则总的数量取出来
            int totalStep = sequenceRuleParams.size();

            long eventStartTime = sequenceRuleParams.get(0).getEventStartTime();
            long eventEndTime = sequenceRuleParams.get(0).getEventEndTime();

            //开始查询
            if (eventStartTime >= agoSixHourTime) {
                return sequenceRuleService.mathRuleSequenceCount(ruleComplexParam);
            } else if (eventEndTime < agoSixHourTime) {
                return sequenceRuleServiceClickHouseManagerImpl.mathRuleSequenceCount(clientLog, ruleComplexParam);
            } else {
                //需要查询缓存和clickhouse
                //同步sequenceRuleParams组合规则的时间
                resetQueryTime(sequenceRuleParams, agoSixHourTime, eventEndTime, ruleComplexParam);
                if (sequenceRuleService.mathRuleSequenceCount(ruleComplexParam)) return true;

                //查询clickhouse
                resetQueryTime(sequenceRuleParams, eventStartTime, agoSixHourTime, ruleComplexParam);
                if (sequenceRuleServiceClickHouseManagerImpl.mathRuleSequenceCount(clientLog, ruleComplexParam))
                    return true;
                int clickHouseMaxStep = ruleComplexParam.getSequenceRuleLastStep();

                /**
                 * 拼接state里面的匹配结果
                 * */
                resetQueryTime(sequenceRuleParams, agoSixHourTime, eventEndTime, ruleComplexParam);
                //规则还剩余的部分，看是否在State里面是否可以匹配
                ruleComplexParam.setSequenceRuleParams(sequenceRuleParams.subList(clickHouseMaxStep, sequenceRuleParams.size()));
                sequenceRuleService.mathRuleSequenceCount(ruleComplexParam);
                int finalMaxStep = ruleComplexParam.getSequenceRuleLastStep() + clickHouseMaxStep;
                return finalMaxStep >= totalStep;
            }
        }
        return true;
    }

    /**
     * 同步查询的时间
     * */
    private void resetQueryTime(List<RuleEventExecuteParam> sequenceRuleParams,long startTime,long endTime,RuleComplexParam ruleComplexParam) {
        for (RuleEventExecuteParam ruleEventExecuteParam:sequenceRuleParams) {
            ruleEventExecuteParam.setEventStartTime(startTime);
            ruleEventExecuteParam.setEventEndTime(endTime);
        }
        ruleComplexParam.setSequenceRuleParams(sequenceRuleParams);
    }

    /**
     * 封装全局查询方法
     * */
    public boolean isMatchRule(ClientLog clientLog,RuleComplexParam ruleComplexParam) {
        RuleEventExecuteParam trigger = ruleComplexParam.getTrigger();
        if (!RuleCommonProcessUtils.eventMatchRuleParam(clientLog,trigger)) return false;
        log.debug("规则编号：{}，事件类型：{}，满足了触发条件",ruleComplexParam.getRuleId(),clientLog.getEventId());

        //基本规则
        if (!mathRuleByCondition(clientLog,ruleComplexParam)) return false;

        //组合条件
        if (!mathRuleByCondition(clientLog,ruleComplexParam)) return false;

        //顺序条件
        if (!mathRuleSequenceCount(clientLog,ruleComplexParam)) return false;
        return true;
    }

    public boolean isMatchRuleByTimer(String userId,TriggerTimer triggerTimer,long startTime,long endTime) {
        List<CombinationRuleParam> combinationRuleParams = triggerTimer.getCombinationRuleParams();
        for(CombinationRuleParam combinationRuleParam:combinationRuleParams) {
            combinationRuleParam.setStartTime(startTime);
            combinationRuleParam.setEndTime(endTime);
            //查询，State,hbase,clickhouse
        }
        return true;
    }
}